package cmd

import (
    "encoding/json"
    "errors"
    "fmt"
    "os"
    `path`
    "regexp"
    "strings"

    "gitee.com/exi-red/maketools/util"
    "github.com/spf13/cobra"
)

const (
    // Air 子命令
    Air = "air"

    // AirDesc 描述
    AirDesc = "更新 .air.toml 文件的关键配置"
)

// AirFlags air 子命令参数结构
type AirFlags struct {
    File    Params // air 配置文件
    Cmd     Params // air 编译命令
    Bin     Params // air 运行命令
    BinArgs Params // air 运行命令的参数
    TmpDir  Params // air 编译、运行临时目录
    GCflags Params // go build 编译器参数
    LDflags Params // go build 链接器参数
}

// AirFlag air 子命令参数
var AirFlag = AirFlags{
    File: Params{
        Name:      "file",
        Shorthand: "f",
        Value:     ".air.toml",
        Usage:     "air 配置文件",
    },
    Cmd: Params{
        Name:      "cmd",
        Shorthand: "c",
        Value:     "",
        Usage:     "air 编译命令",
    },
    Bin: Params{
        Name:      "bin",
        Shorthand: "b",
        Value:     "",
        Usage:     "air 运行命令",
    },
    BinArgs: Params{
        Name:      "bin-args",
        Shorthand: "a",
        Value:     "",
        Usage:     "air 运行命令的参数",
    },
    TmpDir: Params{
        Name:      "tmp-dir",
        Shorthand: "t",
        Value:     "",
        Usage:     "air 编译、运行临时目录",
    },
    GCflags: Params{
        Name:      "gcflags",
        Shorthand: "g",
        Value:     "",
        Usage:     "go build 编译器参数，cmd 参数中使用 {gcflags} 变量接收",
    },
    LDflags: Params{
        Name:      "ldflags",
        Shorthand: "l",
        Value:     "",
        Usage:     "go build 链接器参数，cmd 参数中使用 {ldflags} 变量接收",
    },
}

// 注册子命令 Air
var airCmd = &cobra.Command{
    Use:   Air,
    Short: AirDesc,
    Long:  util.CLIHeader(40, AirDesc),
    Example: strings.NewReplacer(
        "{ROOTNAME}", ROOTNAME,
        "{air}", Air,
        "{File}", AirFlag.File.Name,
        "{Cmd}", AirFlag.Cmd.Name,
        "{Bin}", AirFlag.Bin.Name,
        "{BinArgs}", AirFlag.BinArgs.Name,
        "{GCflags}", AirFlag.GCflags.Name,
        "{LDflags}", AirFlag.LDflags.Name,
        "{TmpDir}", AirFlag.TmpDir.Name,
    ).Replace(
        `  {ROOTNAME} {air} ^ 
    --{File}     ".air.toml" ^ 
    --{Cmd}      "go build ..." ^ 
    --{Bin}      ".\run.exe" ^ 
    --{BinArgs} "--port 80" ^ 
    --{GCflags}  "-N -l" ^ 
    --{LDflags}  "-s -w" ^ 
    --{TmpDir}  "dist"`,
    ),
    Run: func(cmd *cobra.Command, args []string) {
        // 处理器执行
        err := airHanlder(cmd)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
    },
}

// 初始化
func init() {
    // 禁用自动排序
    airCmd.Flags().SortFlags = false

    // 解析参数
    StringP(airCmd, AirFlag.File)
    StringP(airCmd, AirFlag.Cmd)
    StringP(airCmd, AirFlag.Bin)
    StringP(airCmd, AirFlag.BinArgs)
    StringP(airCmd, AirFlag.GCflags)
    StringP(airCmd, AirFlag.LDflags)
    StringP(airCmd, AirFlag.TmpDir)

    // 自定义帮助信息
    BoolP(airCmd, "help", "h", false, "显示 air 命令的帮助")
}

// 复制资源处理器
func airHanlder(cmd *cobra.Command) error {
    flags := cmd.Flags()

    // air 配置文件路径
    file, _ := flags.GetString(AirFlag.File.Name)
    if file == "" {
        return errors.New("no configuration file specified")
    }
    file = util.PathFormat(file)

    // air 编译命令
    airCmd, _ := flags.GetString(AirFlag.Cmd.Name)
    if airCmd == "" {
        return errors.New("no compilation command specified")
    }
    airCmd = strings.ReplaceAll(airCmd, "\\", "/")
    airCmd = strings.ReplaceAll(airCmd, "&& ", "\n")
    airCmd = strings.ReplaceAll(airCmd, "&&", "\n")

    // air 运行命令
    run, _ := flags.GetString(AirFlag.Bin.Name)
    if run == "" {
        return errors.New("no run command specified")
    }
    run = strings.ReplaceAll(run, "\\", "/")

    // air 运行命令的参数
    runArg, _ := flags.GetString(AirFlag.BinArgs.Name)
    runArgs := make([]string, 0)
    if runArg != "" {
        for _, arg := range strings.Split(runArg, " ") {
            arg = strings.TrimSpace(arg)
            if arg != "" {
                runArgs = append(runArgs, arg)
            }
        }
    }
    stringRunArgs, _ := json.Marshal(runArgs)

    // air 编译、运行临时目录
    tmpDir, _ := flags.GetString(AirFlag.TmpDir.Name)
    if tmpDir == "" {
        tmpDir = "build" // 默认
    }
    tmpDir = strings.ReplaceAll(tmpDir, "\\", "/")

    // go build 编译器参数
    GCflags, _ := flags.GetString(AirFlag.GCflags.Name)
    if GCflags != "" {
        // 替换 cmd 命令中的变量为 ps1 变量
        airCmd = strings.ReplaceAll(airCmd, "{gcflags}", "$gcflags")
    }

    // go build 链接器参数
    LDflags, _ := flags.GetString(AirFlag.LDflags.Name)
    if LDflags != "" {
        // 替换 cmd 命令中的变量为 ps1 变量
        airCmd = strings.ReplaceAll(airCmd, "{ldflags}", "$ldflags")
    }

    // --- 新建临时文件夹及.ps1文件 ---
    subTmpDir := path.Join(tmpDir, "temp")
    err := os.MkdirAll(subTmpDir, 0644)
    if err != nil {
        return err
    }

    // ps1 文件
    ps1File := path.Join(subTmpDir, "air.ps1")
    ps1Cont := strings.NewReplacer(
        "{ldflags}", LDflags,
        "{gcflags}", GCflags,
        "{cmd}", airCmd,
    ).Replace(
        `$ldflags = "{ldflags}"
$gcflags = "{gcflags}"
{cmd}`,
    )
    // 写 ps1 文件
    err = os.WriteFile(ps1File, []byte(ps1Cont), 0644)
    if err != nil {
        return err
    }

    // air.toml 真实使用的 cmd
    airRealCmd := strings.NewReplacer(
        "{ps1File}", ps1File,
    ).Replace("powershell.exe -ExecutionPolicy Bypass -File {ps1File}")

    // 读取 .air.toml 文件
    content, err := os.ReadFile(file)
    if err != nil {
        return err
    }

    // 匹配 "hello" 开头的行
    cmdRe := regexp.MustCompile(`(?m)(?m)^\s?(cmd)\s?=\s?(.*?)(# \[cmd] DO NOT DELETE #)`)
    binRe := regexp.MustCompile(`(?m)(?m)^\s?(bin)\s?=\s?(.*?)(# \[bin] DO NOT DELETE #)`)
    argRe := regexp.MustCompile(`(?s)(?m)^\s?(args_bin)\s?=\s?(.*?)(# \[args_bin] DO NOT DELETE #)`)
    tmpDirRe := regexp.MustCompile(`(?s)(?m)^\s?(tmp_dir)\s?=\s?(.*?)(# \[tmp_dir] DO NOT DELETE #)`)

    // 替换所有匹配的行
    newContent := string(content)
    newContent = cmdRe.ReplaceAllString(newContent, fmt.Sprintf(`$1 = "%s" $3`, airRealCmd))
    newContent = binRe.ReplaceAllString(newContent, fmt.Sprintf(`$1 = "%s" $3`, run))
    newContent = argRe.ReplaceAllString(newContent, fmt.Sprintf(`$1 = %s $3`, string(stringRunArgs)))
    newContent = tmpDirRe.ReplaceAllString(newContent, fmt.Sprintf(`$1 = "%s" $3`, tmpDir))

    // 写回文件
    err = os.WriteFile(file, []byte(newContent), 0644)
    if err != nil {
        return err
    }
    return nil
}
